#include <iconv.h>
#include <stdlib.h>
#include <langinfo.h>
#include <assert.h>
#include <string.h>

#include "tdebug.h"
#include "tstring.h"
#include "localehelper.h"

#define TAGLIB_COMPAT_ENCODING_ENV_NAME "TAGLIB_COMPAT_ENCODING"

namespace LocaleHelper
{

using namespace TagLib;

static bool innerIconv(const char *fenc, const char *toenc, const char *src, size_t ssize,
    char **out, size_t *osize)
{
    assert(out != NULL && osize != NULL);
   
    if(!ssize)
    {
        return true;
    }
    
    iconv_t cnv = iconv_open(toenc, fenc);
    if((iconv_t)-1 == cnv)
    {
        return false;
    }
    
    size_t inbufsize = ssize;
    size_t obufsize = (inbufsize + 1) * 6;
    
    char *inbuf = new char[inbufsize];
    char *obuf = new char[obufsize];
    
    char *tmpinbuf = inbuf;
    char *tmpobuf = obuf;
    
    size_t inbufleft = ssize;
    size_t obufleft = obufsize;

    memcpy(inbuf, src, ssize);
    size_t st = iconv(cnv, &tmpinbuf, &inbufleft, &tmpobuf, &obufleft);
    if(st == (size_t)-1)
    {
        delete [] inbuf;
        delete [] obuf;
        return false;
    }
    else
    {
        *out = obuf;
        *osize = obufsize - obufleft;
        delete [] inbuf;
        iconv_close(cnv);
        return true;
    }
}

bool guessIconv(const char *data, size_t size, ByteVector *out)
{
    assert(out != NULL);
    
    std::string encname;
    
    // get encoding name
    const char *ptmp = getenv(TAGLIB_COMPAT_ENCODING_ENV_NAME);
    if(NULL == ptmp)
    {
        ptmp = nl_langinfo(CODESET);
        if(NULL == ptmp)
        {
            debug("guessIconv() -- Get system current codeset error");
            return false;
        }
        else
        {
            encname = ptmp;
        }
    }
    else
    {
        encname = ptmp;
    }
    
    
    // Guess to iconv
    char *picvout = NULL;
    size_t icvoutsize = 0;
    if(!innerIconv(encname.c_str(), "UTF8", data, size, &picvout, &icvoutsize)) // First try to iconv
    {
        // Second try to iconv
        debug("guessIconv() -- First guess iconv error, force try to used GBK encoding");
        if(!innerIconv("GBK", "UTF8", data, size, &picvout, &icvoutsize))
        {
            debug("guessIconv() -- Second guess iconv error, force try to used UTF8 encoding");
            
            if(!innerIconv("UTF8", "UTF8", data, size, &picvout, &icvoutsize))
            {
                debug("guessIconv() -- Second guess iconv error, force to used default");
                return false;
            }
        }
    }
    
    if(picvout != NULL)
    {
        *out = ByteVector(picvout, icvoutsize);
        delete [] picvout;
    }
    
    return true;
}

bool guessIconv(const TagLib::ByteVector &bv, TagLib::ByteVector *out)
{
    return guessIconv(bv.data(), bv.size(), out);
}

}

